home *** CD-ROM | disk | FTP | other *** search
/ Gamers Delight 2 / Gamers Delight 2.iso / Aminet / game / role / AMScen_0_9.lha / AMScen / news.m < prev    next >
Text File  |  1995-01-21  |  25KB  |  984 lines

  1. /*
  2.  * Amiga MUD
  3.  *
  4.  * Copyright (c) 1995 by Chris Gray
  5.  */
  6.  
  7. /*
  8.  * news.m - allow the player to interact with usenet news.
  9.  */
  10.  
  11. /* NOTE: this current setup is intended for use with Matt Dillon's UUCP
  12.    and news distribution, version 1.16 or later. To use it with other
  13.    forms of news, you will perhaps need to change various things.
  14.    Dependencies: UULIB:NewsGroups, a hierarchical set of news directories
  15.    in UUNEWS:, postnews, T: */
  16.  
  17. use t_streets
  18.  
  19. /* The following property is actually added to SysAdmin. It is used as a
  20.    global variable to check whether this MUD allows players to set their
  21.    own realnames. Since mail/news can go all over the world, many sysadmins
  22.    may prefer that everything that originates with their site at least have
  23.    a valid and traceable name attached to it. Set the value to 'true' if
  24.    you want people to be able to change their own realnames. Think seriously
  25.    about it before you do it. */
  26.  
  27. define tp_news p_pCanChangeName CreateBoolProp().
  28. CharacterThing(Character("SysAdmin"))@p_pCanChangeName := false.
  29.  
  30. define tp_news NewsThing CreateThing(nil).
  31. define tp_news p_vNewsParse CreateActionProp().
  32. define tp_news p_vNewsGroupParse CreateActionProp().
  33. define tp_news p_vNewsHeaderParse CreateActionProp().
  34. define tp_news p_vNewsBodyParse CreateActionProp().
  35.  
  36. define tp_news g_news CreateGrammar().
  37.  
  38. define tp_news p_pNRealName CreateStringProp().
  39. define tp_news p_pNSaveHandler CreateActionProp().
  40. define tp_news p_pNSaveIdleHandler CreateActionProp().
  41. define tp_news p_pNSavePrompt CreateStringProp().
  42. define tp_news p_pNGroupList CreateThingListProp().
  43. define tp_news p_pNCurrentGroup CreateIntProp().
  44. define tp_news p_pNUnreadIndex CreateIntProp().
  45. define tp_news p_pNCurrentArticle CreateIntProp().
  46. define tp_news p_pNFd CreateIntProp().
  47. define tp_news p_pNString CreateStringProp().
  48. define tp_news p_pNString2 CreateStringProp().
  49. define tp_news p_pNSubject CreateStringProp().
  50. define tp_news p_pNEndAction CreateActionProp().
  51.  
  52. define tp_news p_NGroupName CreateStringProp().
  53. define tp_news p_NHighestRead CreateIntProp().
  54. define tp_news p_NLowest CreateIntProp().
  55. define tp_news p_NHighest CreateIntProp().
  56. define tp_news p_NUnread CreateIntListProp().
  57.  
  58. CharacterThing(SysAdmin)@p_pNRealName := "MUD administrator".
  59.  
  60. define tp_news proc nv_help()bool:
  61.     Print(
  62. "Commands in the newsroom are:\n\n"
  63. "  bye/quit - exit from the program\n"
  64. "  exit/leave/out - leave the newsroom\n"
  65. "  info - some additional comments on MUD news\n"
  66. "  groups/newsgroups - print the available groups\n"
  67. "  subscribe/sub - print the groups you are subscribed to\n"
  68. "  subscribe/sub \"group\" - subscribe to the given group\n"
  69. "  unsubscribe/unsub \"group\" - unsubscribe from the given group\n"
  70. "  catchup \"group\" - mark all current articles as read\n"
  71. "  read - start reading news\n"
  72. "  name/realname <name> - set your real name to <name>\n"
  73. "  post \"group\" [subject] - post an article to the given group\n"
  74.     );
  75.     true
  76. corp;
  77.  
  78. Verb0(g_news, "help", 0, nv_help).
  79. Synonym(g_news, "help", "?").
  80.  
  81. define tp_news proc nv_info()bool:
  82.  
  83.     Print(
  84. "Newsgroup names almost always contain periods, so you must enclose them in "
  85. "quotes to prevent MUD's normal parsing from treating e.g.\n"
  86. "    subscribe rec.games.mud\n"
  87. "as 'subscribe rec' followed by 'games' followed by 'mud'. The optional "
  88. "subject on the 'post' command can be quoted or not, depending on whether "
  89. "you need to put punctuation in it. Similarly, your real name on the 'name' "
  90. "command can be quoted or not. The MUD newsreader is quite primitive - it "
  91. "doesn't understand message threads, followups, replies, etc. If you need to "
  92. "use usenet news a lot, you should try to get a normal usenet connection.\n"
  93.     );
  94.     true
  95. corp;
  96.  
  97. Verb0(g_news, "info", 0, nv_info).
  98.  
  99. define tp_news proc nv_groups()bool:
  100.     int fd, blank;
  101.     string line;
  102.     bool first;
  103.  
  104.     fd := FileOpenForRead("UULIB:NewsGroups");
  105.     if fd = 0 then
  106.     Print("This MUD is not set up to offer news reading.\n");
  107.     else
  108.     Print("Newsgroups available on this machine:\n");
  109.     first := true;
  110.     while
  111.         line := FileRead(fd);
  112.         line ~= ""
  113.     do
  114.         blank := Index(line, " ");
  115.         if blank < 0 then
  116.         blank := Index(line, "\t");
  117.         fi;
  118.         if blank > 0 then
  119.         line := SubString(line, 0, blank);
  120.         fi;
  121.         if not line == "Junk" then
  122.         if first then
  123.             first := false;
  124.         else
  125.             Print(", ");
  126.         fi;
  127.         Print(line);
  128.         fi;
  129.     od;
  130.     Print("\n");
  131.     FileClose(fd);
  132.     fi;
  133.     true
  134. corp;
  135.  
  136. Verb0(g_news, "groups", 0, nv_groups).
  137. Synonym(g_news, "groups", "group");
  138. Synonym(g_news, "groups", "newsgroups").
  139.  
  140. define tp_news proc nv_subscribe(string group)bool:
  141.     thing me, th;
  142.     int count, n, fd;
  143.     list thing lt;
  144.     string line;
  145.  
  146.     me := Me();
  147.     lt := me@p_pNGroupList;
  148.     if group = "" then
  149.     if lt = nil or Count(lt) = 0 then
  150.         Print("You are not currently subscribed to any groups.\n");
  151.     else
  152.         Print("You are currently subscribed to the following groups:\n");
  153.         count := Count(lt);
  154.         n := 0;
  155.         while n ~= count do
  156.         Print("  ");
  157.         Print(lt[n]@p_NGroupName);
  158.         Print("\n");
  159.         n := n + 1;
  160.         od;
  161.     fi;
  162.     else
  163.     fd := FileOpenForRead("UULIB:NewsGroups");
  164.     if fd = 0 then
  165.         Print("This MUD is not set up to offer news reading.\n");
  166.     else
  167.         if lt = nil then
  168.         lt := CreateThingList();
  169.         me@p_pNGroupList := lt;
  170.         fi;
  171.         while
  172.         line := FileRead(fd);
  173.         if line = "" then
  174.             false
  175.         else
  176.             n := Index(line, " ");
  177.             if n < 0 then
  178.             n := Index(line, "\t");
  179.             fi;
  180.             if n > 0 then
  181.             line := SubString(line, 0, n);
  182.             fi;
  183.             not line == group
  184.         fi
  185.         do
  186.         od;
  187.         FileClose(fd);
  188.         if line = "" then
  189.         Print(
  190.             "This MUD does not have group \"" + group + "\".\n"
  191.             "You probably need to enclose the group name in quotes.\n"
  192.         );
  193.         elif FindName(me@p_pNGroupList, p_NGroupName, group) ~= fail then
  194.         Print("You already subscribe to group \"" + group + "\".\n");
  195.         else
  196.         th := CreateThing(nil);
  197.         th@p_NGroupName := group;
  198.         th@p_NHighestRead := 0;
  199.         th@p_NUnread := CreateIntList();
  200.         th@p_NLowest := 0;
  201.         th@p_NHighest := 0;
  202.         AddTail(lt, th);
  203.         Print("Subscribed to group \"" + group + "\".\n");
  204.         fi;
  205.     fi;
  206.     fi;
  207.     true
  208. corp;
  209.  
  210. Verb1(g_news, "subscribe", 0, nv_subscribe).
  211. Synonym(g_news, "subscribe", "sub").
  212.  
  213. define tp_news proc nv_unsubscribe(string name)bool:
  214.     thing me, group;
  215.     list thing lt;
  216.  
  217.     if name = "" then
  218.     Print("You must specify a group to unsubscribe from.\n");
  219.     false
  220.     else
  221.     me := Me();
  222.     lt := me@p_pNGroupList;
  223.     if lt = nil or FindName(me@p_pNGroupList, p_NGroupName, name) = fail
  224.     then
  225.         Print("You are not subscribed to group \"" + name + "\".\n");
  226.     else
  227.         group := FindResult();
  228.         DelElement(lt, group);
  229.         ClearThing(group);
  230.         Print("Unsubscribed from group \"" + name + "\".\n");
  231.     fi;
  232.     true
  233.     fi
  234. corp;
  235.  
  236. Verb1(g_news, "unsubscribe", 0, nv_unsubscribe).
  237. Synonym(g_news, "unsubscribe", "unsub").
  238.  
  239. define tp_news proc openDotNextFile(string name)int:
  240.     int i;
  241.     string path;
  242.  
  243.     path := name;
  244.     while
  245.     i := Index(path, ".");
  246.     i ~= -1
  247.     do
  248.     path := StringReplace(path, i, "/");
  249.     od;
  250.     i := FileOpenForRead("UUNEWS:" + path + "/.next");
  251.     if i ~= 0 then
  252.     /* Grrr. This was not needed in older UUCP stuff! Note that it WILL
  253.        work for multiple simultaneous readers, since this whole set of
  254.        stuff is done atomically for a given client. */
  255.     FileClose(i);
  256.     i := FileOpenForWrite("t:crfile");
  257.     FileWrite(i, "\n");
  258.     FileClose(i);
  259.     Execute("delete t:.next");
  260.     Execute("join UUNEWS:" + path + "/.next t:crfile as t:.next");
  261.     i := FileOpenForRead("t:.next");
  262.     fi;
  263.     i
  264. corp;
  265.  
  266. define tp_news proc openArticleFile(string name; int article)int:
  267.     int i;
  268.     string path;
  269.  
  270.     path := name;
  271.     while
  272.     i := Index(path, ".");
  273.     i ~= -1
  274.     do
  275.     path := StringReplace(path, i, "/");
  276.     od;
  277.     FileOpenForRead("UUNEWS:" + path + "/" + IntToString(article))
  278. corp;
  279.  
  280. define tp_news proc nv_catchUp(string name)bool:
  281.     thing me, group;
  282.     list thing lt;
  283.     int fd, nextArticle;
  284.     string line;
  285.  
  286.     if name = "" then
  287.     Print("You must specify a group to catchup in.\n");
  288.     false
  289.     else
  290.     me := Me();
  291.     lt := me@p_pNGroupList;
  292.     if lt = nil or FindName(me@p_pNGroupList, p_NGroupName, name) = fail
  293.     then
  294.         Print("You are not subscribed to group \"" + name + "\".\n");
  295.     else
  296.         group := FindResult();
  297.         fd := openDotNextFile(name);
  298.         if fd = 0 then
  299.         Print("No such newsgroup???\n");
  300.         else
  301.         line := FileRead(fd);
  302.         FileClose(fd);
  303.         nextArticle := StringToPosInt(line);
  304.         if nextArticle <= 0 then
  305.             Print("Something wrong in that newsgroup!\n");
  306.         elif nextArticle = 1 then
  307.             Print("No articles to catch up yet!\n");
  308.         else
  309.             group@p_NHighestRead := nextArticle - 1;
  310.             Print("Caught up in group \"" + name + "\".\n");
  311.         fi;
  312.         fi;
  313.     fi;
  314.     true
  315.     fi
  316. corp;
  317.  
  318. Verb1(g_news, "catchup", 0, nv_catchUp).
  319.  
  320. define tp_news proc newsFindNextGroup()bool:
  321.     list thing lt;
  322.     thing me, group;
  323.     int which, groupCount, fd, nextArticle, cnt, lo, hi, mid;
  324.     string line, name, prompt;
  325.     list int li;
  326.     bool gotOne;
  327.  
  328.     me := Me();
  329.     lt := me@p_pNGroupList;
  330.     groupCount := Count(lt);
  331.     gotOne := false;
  332.     which := me@p_pNCurrentGroup;
  333.     while
  334.     if which >= groupCount then
  335.         false
  336.     else
  337.         group := lt[which];
  338.         name := group@p_NGroupName;
  339.         fd := openDotNextFile(name);
  340.         if fd = 0 then
  341.         nextArticle := 0;
  342.         else
  343.         line := FileRead(fd);
  344.         FileClose(fd);
  345.         nextArticle := StringToPosInt(line);
  346.         fi;
  347.         if nextArticle <= 1 then
  348.         true
  349.         else
  350.         lo := group@p_NHighestRead;
  351.         hi := nextArticle - 1;
  352.         while lo < hi do
  353.             mid := (lo + hi) / 2;
  354.             fd := openArticleFile(name, mid);
  355.             if fd = 0 then
  356.             lo := mid + 1;
  357.             else
  358.             FileClose(fd);
  359.             hi := mid - 1;
  360.             fi;
  361.         od;
  362.         /* sometimes miss by one */
  363.         fd := openArticleFile(name, lo);
  364.         if fd = 0 then
  365.             lo := lo + 1;
  366.         else
  367.             FileClose(fd);
  368.         fi;
  369.         group@p_NLowest := lo;
  370.         lo := lo - 1;
  371.         if group@p_NHighestRead < lo then
  372.             group@p_NHighestRead := lo;
  373.         fi;
  374.         hi := nextArticle - 1;
  375.         group@p_NHighest := hi;
  376.         lo := group@p_NHighestRead;
  377.         prompt := "Group " + name + " has ";
  378.         if lo < hi then
  379.             gotOne := true;
  380.             prompt := prompt + IntToString(hi - lo) + " new";
  381.         fi;
  382.         li := group@p_NUnread;
  383.         cnt := Count(li);
  384.         mid := 0;
  385.         lo := 0;
  386.         hi := 0;
  387.         while hi < cnt do
  388.             mid := li[hi];
  389.             fd := openArticleFile(name, mid);
  390.             if fd ~= 0 then
  391.             FileClose(fd);
  392.             lo := lo + 1;
  393.             fi;
  394.             hi := hi + 1;
  395.         od;
  396.         if lo ~= 0 then
  397.             if not gotOne then
  398.             gotOne := true;
  399.             else
  400.             prompt := prompt + " and ";
  401.             fi;
  402.             prompt := prompt + IntToString(lo) + " old";
  403.         fi;
  404.         if gotOne then
  405.             Print(prompt + " unread articles.\n");
  406.             false
  407.         else
  408.             true
  409.         fi
  410.         fi
  411.     fi
  412.     do
  413.     which := which + 1;
  414.     od;
  415.     me@p_pNCurrentGroup := which;
  416.     gotOne
  417. corp;
  418.  
  419. define tp_news proc newsAllDone()void:
  420.  
  421.     ignore SetPrompt("newsroom> ");
  422.     ignore SetCharacterInputAction(NewsThing@p_vNewsParse);
  423. corp;
  424.  
  425. define tp_news proc newsReadGroup()void:
  426.  
  427.     Me()@p_pNUnreadIndex := 0;
  428.     ignore SetPrompt("Rnq? ");
  429.     ignore SetCharacterInputAction(NewsThing@p_vNewsGroupParse);
  430. corp;
  431.  
  432. define tp_news proc newsNextGroup()void:
  433.  
  434.     Me()@p_pNCurrentGroup := Me()@p_pNCurrentGroup + 1;
  435.     if newsFindNextGroup() then
  436.     newsReadGroup();
  437.     else
  438.     newsAllDone();
  439.     fi;
  440. corp;
  441.  
  442. define tp_news proc newsFindNextArticle()bool:
  443.     thing me, group;
  444.     int article;
  445.     list int unread;
  446.     string line;
  447.  
  448.     me := Me();
  449.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  450.     article := me@p_pNUnreadIndex;
  451.     unread := group@p_NUnread;
  452.     if article >= Count(unread) then
  453.     article := group@p_NHighestRead + 1;
  454.     if article > group@p_NHighest then
  455.         false
  456.     else
  457.         me@p_pNCurrentArticle := article;
  458.         true
  459.     fi
  460.     else
  461.     me@p_pNCurrentArticle := unread[article];
  462.     true
  463.     fi
  464. corp;
  465.  
  466. define tp_news proc newsShowHeader()bool:
  467.     thing me;
  468.     int article, fd, colon;
  469.     string line, header;
  470.  
  471.     me := Me();
  472.     fd := openArticleFile(
  473.         me@p_pNGroupList[me@p_pNCurrentGroup]@p_NGroupName,
  474.         me@p_pNCurrentArticle);
  475.     if fd ~= 0 then
  476.     while
  477.         line := FileRead(fd);
  478.         if line = "" then
  479.         false
  480.         else
  481.         colon := Index(line, ":");
  482.         if colon <= 0 then
  483.             false
  484.         else
  485.             header := SubString(line, 0, colon);
  486.             Index(header, " ") < 0
  487.         fi
  488.         fi
  489.     do
  490.         Print(line);
  491.         Print("\n");
  492.     od;
  493.     me@p_pNFd := fd;
  494.     if line ~= "" then
  495.         me@p_pNString := line;
  496.     fi;
  497.     true
  498.     else
  499.     false
  500.     fi
  501. corp;
  502.  
  503. define tp_news proc newsNextArticle()void:
  504.     thing me, group;
  505.     int index;
  506.  
  507.     me := Me();
  508.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  509.     if newsFindNextArticle() then
  510.     if newsShowHeader() then
  511.         ignore SetPrompt("Rnsq? ");
  512.         ignore SetCharacterInputAction(NewsThing@p_vNewsHeaderParse);
  513.     else
  514.         Print("Hmm. Article has disappeared!\n");
  515.     fi;
  516.     else
  517.     Print("End of group " + group@p_NGroupName + ".\n");
  518.     newsNextGroup();
  519.     fi;
  520. corp;
  521.  
  522. define tp_news proc newsDoneArticle()void:
  523.     thing me, group;
  524.     int index;
  525.  
  526.     me := Me();
  527.     index := me@p_pNUnreadIndex;
  528.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  529.     if index < Count(group@p_NUnread) then
  530.     RemHead(group@p_NUnread);
  531.     else
  532.     group@p_NHighestRead := group@p_NHighestRead + 1;
  533.     fi;
  534.     newsNextArticle();
  535. corp;
  536.  
  537. define tp_news proc newsFileClose()void:
  538.     int fd;
  539.  
  540.     fd := Me()@p_pNFd;
  541.     if fd ~= 0 then
  542.     FileClose(fd);
  543.     Me()@p_pNFd := 0;
  544.     fi;
  545. corp;
  546.  
  547. define tp_news proc newsUnreadArticle()void:
  548.     thing me, group;
  549.  
  550.     me := Me();
  551.     group := me@p_pNGroupList[me@p_pNCurrentGroup];
  552.     if me@p_pNUnreadIndex >= Count(group@p_NUnread) then
  553.     AddTail(group@p_NUnread, me@p_pNCurrentArticle);
  554.     me@p_pNUnreadIndex := me@p_pNUnreadIndex + 1;
  555.     group@p_NHighestRead := group@p_NHighestRead + 1;
  556.     fi;
  557.     Print("Marking article as unread.\n");
  558.     newsNextArticle();
  559. corp;
  560.  
  561. define tp_news proc newsEndParse(string line)void:
  562.  
  563.     if line = "" or line == "n" then
  564.     newsDoneArticle();
  565.     elif line == "s" then
  566.     newsUnreadArticle();
  567.     elif line == "q" then
  568.     newsNextGroup();
  569.     else
  570.     Print("Options are:\n"
  571.         "  n - go on to next article\n"
  572.         "  s - mark article as unread\n"
  573.         "  q - exit from this group\n"
  574.         "Empty line is equivalent to 'n'\n"
  575.     );
  576.     fi;
  577. corp;
  578.  
  579. define tp_news proc newsShowPage()bool:
  580.     thing me;
  581.     int fd, length, n, width, len;
  582.     string line;
  583.  
  584.     me := Me();
  585.     fd := me@p_pNFd;
  586.     length := TextHeight(0);
  587.     width := TextWidth(0);
  588.     line := me@p_pNString;
  589.     me -- p_pNString;
  590.     n := 1;
  591.     while
  592.     if n >= length - 1 then
  593.         false
  594.     else
  595.         if line = "" then
  596.         line := FileRead(fd);
  597.         fi;
  598.         if line = "" then
  599.         FileClose(fd);
  600.         me@p_pNFd := 0;
  601.         ignore SetPrompt("Nsq? ");
  602.         ignore SetCharacterInputAction(newsEndParse);
  603.         false
  604.         else
  605.         true
  606.         fi
  607.     fi
  608.     do
  609.     Print(line);
  610.     Print("\n");
  611.     len := Length(line);
  612.     if len <= width then
  613.         n := n + 1;
  614.     else
  615.         n := n + (len + width - 4) / (width - 9);
  616.     fi;
  617.     line := "";
  618.     od;
  619.     line ~= ""
  620. corp;
  621.  
  622. define tp_news proc newsBodyParse(string line)void:
  623.  
  624.     if line = "" or line == "c" then
  625.     ignore newsShowPage();
  626.     elif line == "n" then
  627.     newsFileClose();
  628.     newsDoneArticle();
  629.     elif line == "s" then
  630.     newsFileClose();
  631.     newsUnreadArticle();
  632.     elif line == "q" then
  633.     newsFileClose();
  634.     newsNextGroup();
  635.     else
  636.     Print("Options are:\n"
  637.         "  c - continue with article\n"
  638.         "  n - go on to next article\n"
  639.         "  s - mark article as unread\n"
  640.         "  q - exit from this group\n"
  641.         "Empty line is equivalent to 'c'\n"
  642.     );
  643.     fi;
  644. corp;
  645.  
  646. define tp_news proc newsHeaderParse(string line)void:
  647.  
  648.     if line = "" or line == "r" or line == "y" then
  649.     if newsShowPage() then
  650.         ignore SetCharacterInputAction(newsBodyParse);
  651.         ignore SetPrompt("[M O R E] Cnsq? ");
  652.     fi;
  653.     elif line == "n" then
  654.     newsFileClose();
  655.     newsDoneArticle();
  656.     elif line == "s" then
  657.     newsFileClose();
  658.     newsUnreadArticle();
  659.     elif line == "q" then
  660.     newsFileClose();
  661.     newsNextGroup();
  662.     else
  663.     Print("Options are:\n"
  664.         "  r - read the body of this article\n"
  665.         "  n - go on to next article\n"
  666.         "  s - mark article as unread\n"
  667.         "  q - exit from this group\n"
  668.         "Empty line is equivalent to 'r'\n"
  669.     );
  670.     fi;
  671. corp;
  672.  
  673. define tp_news proc newsGroupParse(string line)void:
  674.  
  675.     if line = "" or line == "r" then
  676.     newsNextArticle();
  677.     elif line == "n" then
  678.     newsNextGroup();
  679.     elif line == "q" then
  680.     newsAllDone();
  681.     else
  682.     Print("Options are:\n"
  683.         "  r - read this group\n"
  684.         "  n - go to next group with unread articles\n"
  685.         "  q - exit to top level\n"
  686.         "Empty line is equivalent to 'r'\n"
  687.     );
  688.     fi;
  689. corp;
  690.  
  691. define tp_news proc nv_read()bool:
  692.     thing me;
  693.  
  694.     me := Me();
  695.     if me@p_pNGroupList ~= nil then
  696.     me@p_pNCurrentGroup := 0;
  697.     if newsFindNextGroup() then
  698.         newsReadGroup();
  699.     else
  700.         Print("No unread news in subscribed-to groups.\n");
  701.     fi;
  702.     else
  703.     Print("You are not subscribed to any groups.\n");
  704.     fi;
  705.     true
  706. corp;
  707.  
  708. Verb0(g_news, "read", 0, nv_read).
  709.  
  710. define tp_news proc newsParse(string input)void:
  711.     string word;
  712.  
  713.     SetTail(input);
  714.     word := GetWord();
  715.     if word ~= "" then
  716.     if FindAnyWord(g_news, word) ~= 0 then
  717.         ignore Parse(g_news, input);
  718.     else
  719.         ignore Parse(G, input);
  720.     fi;
  721.     fi;
  722. corp;
  723.  
  724. NewsThing@p_vNewsParse := newsParse.
  725. NewsThing@p_vNewsGroupParse := newsGroupParse.
  726. NewsThing@p_vNewsHeaderParse := newsHeaderParse.
  727. NewsThing@p_vNewsBodyParse := newsBodyParse.
  728.  
  729. define tp_news proc newsPostArticle(string s)void:
  730.     int fd;
  731.     thing me;
  732.  
  733.     me := Me();
  734.     if s = "" then
  735.     Print("Empty article - not posted.\n");
  736.     else
  737.     /* note: server runs atomically, so no conflict over the temp file */
  738.     fd := FileOpenForWrite("T:MUD.article");
  739.     if fd = 0 then
  740.         Print("Sorry - can't open article file.\n");
  741.     else
  742.         /* you might be tempted to put a 'Run' in front of this command,
  743.            so that the player is not hung waiting for an automatic
  744.            'batchnews' run. Beware, however, since doing so might allow
  745.            'batchnews' to be run twice at the same time, with the
  746.            resultant news duplication. */
  747.         Print("Posting article (could take a while) ...\n");
  748.         OPrint("");     /* force a flush */
  749.         Log(me@p_pName + " posting article to " + me@p_pNString2 + "\n");
  750.         FileWrite(fd, "Newsgroups: " + me@p_pNString2 + "\n");
  751.         me -- p_pNString2;
  752.         if me@p_pNSubject ~= "" then
  753.         FileWrite(fd, "Subject: " + me@p_pNSubject + "\n");
  754.         me -- p_pNSubject;
  755.         fi;
  756.         FileWrite(fd, "X-NewsSoftware: AmigaMUD newsroom\n");
  757.         FileWrite(fd, "\n");
  758.         FileWrite(fd, s);
  759.         FileClose(fd);
  760.         Execute("postnews < T:MUD.article -f " + FormatName(me@p_pName) +
  761.             " -r \"" + me@p_pNRealName + "\""
  762.         );
  763.         Execute("delete T:MUD.article");
  764.         Print("... done!\n");
  765.     fi;
  766.     fi;
  767. corp;
  768.  
  769. define tp_news proc nv_post()bool:
  770.     int fd, blank;
  771.     string groupName, line;
  772.  
  773.     groupName := GetWord();
  774.     if groupName = "" then
  775.     Print("You must specify the name of the newsgroup to post to.\n");
  776.     false
  777.     else
  778.     fd := FileOpenForRead("UULIB:NewsGroups");
  779.     if fd = 0 then
  780.         Print("This MUD is not set up to offer news reading.\n");
  781.         false
  782.     elif Me()@p_pNRealName = "" then
  783.         Print("You cannot post until you have a realname set up.\n");
  784.         false
  785.     else
  786.         while
  787.         line := FileRead(fd);
  788.         if line = "" then
  789.             false
  790.         else
  791.             blank := Index(line, " ");
  792.             if blank < 0 then
  793.             blank := Index(line, "\t");
  794.             fi;
  795.             if blank > 0 then
  796.             line := SubString(line, 0, blank);
  797.             fi;
  798.             not line == groupName
  799.         fi
  800.         do
  801.         od;
  802.         FileClose(fd);
  803.         if line = "" then
  804.         Print(
  805.             "This MUD does not have group \"" + groupName + "\".\n"
  806.             "You probably need to enclose the group name in quotes.\n"
  807.         );
  808.         false
  809.         else
  810.         Me()@p_pNString2 := groupName;
  811.         groupName := GetTail();
  812.         if groupName ~= "" then
  813.             if SubString(groupName, 0, 1) = "\"" then
  814.             groupName :=
  815.                 SubString(groupName, 1, Length(groupName) - 2);
  816.             fi;
  817.             Me()@p_pNSubject := groupName;
  818.         fi;
  819.         GetDocument("post> ", "Enter news article", "",
  820.                 newsPostArticle, true)
  821.         fi
  822.     fi
  823.     fi
  824. corp;
  825.  
  826. VerbTail(g_news, "post", nv_post).
  827.  
  828. define tp_news proc nv_name()bool:
  829.     thing me;
  830.     string newName;
  831.  
  832.     if CharacterThing(Character("SysAdmin"))@p_pCanChangeName then
  833.     me := Me();
  834.     newName := GetTail();
  835.     if newName = "" then
  836.         Print(
  837.         "To set up a real name for use with mail and news, use, e.g.\n"
  838.         "   name \"John H. Smith\"\n");
  839.     else
  840.         if Index(newName, "\"") = 0 then
  841.         newName := SubString(newName, 1, Length(newName) - 2);
  842.         fi;
  843.         me@p_pNRealName := newName;
  844.         Print(
  845.         "Your 'real name' is now '" + newName + "'. "
  846.         "This will be used on news articles you post and on letters "
  847.         "that you mail.\n");
  848.     fi;
  849.     true
  850.     else
  851.     Print(
  852.         "The sysadmin of this MUD has not allowed you to set your own "
  853.         "realname. Send a MUD mail message ('write to SysAdmin') to "
  854.         "him/her with your full name so that it can be set up. You will "
  855.         "need a pen and a pad (available from the store in the minimall) "
  856.         "to be able to do this.\n");
  857.     false
  858.     fi
  859. corp;
  860.  
  861. VerbTail(g_news, "name", nv_name).
  862. Synonym(g_news, "name", "realname").
  863.  
  864. define tp_news proc newsIdleHandler()void:
  865.     action a;
  866.  
  867.     newsFileClose();
  868.     newsAllDone();
  869.     Execute("delete t:crfile t:.next");
  870.     a := Me()@p_pNSaveIdleHandler;
  871.     if a ~= nil then
  872.     call(a, void)();
  873.     fi;
  874. corp;
  875.  
  876. define tp_news proc newsEnter()status:
  877.     thing me;
  878.     action a;
  879.  
  880.     me := Me();
  881.     if Character(me@p_pName) = nil then
  882.     /* cannot do this stuff with machines! */
  883.     OPrint(FormatName(me@p_pName) + " will not enter.\n");
  884.     fail
  885.     else
  886.     a := SetCharacterIdleAction(newsIdleHandler);
  887.     if a ~= nil then
  888.         me@p_pNSaveIdleHandler := a;
  889.     fi;
  890.     a := SetCharacterInputAction(newsParse);
  891.     if a ~= nil then
  892.         me@p_pNSaveHandler := a;
  893.     fi;
  894.     me@p_pNSavePrompt := SetPrompt("newsroom> ");
  895.     continue
  896.     fi
  897. corp;
  898.  
  899. define tp_news proc newsExit()status:
  900.     thing me;
  901.  
  902.     me := Me();
  903.     Execute("delete t:crfile t:.next");
  904.     ignore SetCharacterIdleAction(me@p_pNSaveIdleHandler);
  905.     ignore SetCharacterInputAction(me@p_pNSaveHandler);
  906.     ignore SetPrompt(me@p_pNSavePrompt);
  907.     continue
  908. corp;
  909.  
  910. define tp_news r_newsRoom CreateThing(r_indoors).
  911. SetupRoom(r_newsRoom, "in the news room",
  912.     "In this room, the normal commands are replaced by another set which "
  913.     "is used to read and post news articles. You can, however, "
  914.     "use 'west' or 'out' to leave this room. Use 'help' to find out how to "
  915.     "use the service provided here.").
  916. r_newsRoom@p_rNoMachines := true.
  917. Connect(r_ne2, r_newsRoom, D_EAST).
  918. Connect(r_ne2, r_newsRoom, D_ENTER).
  919. UniConnect(r_ne2, r_newsRoom, D_UP).
  920. ExtendDesc(r_ne2,
  921.     "The building here is old, but in good repair. Its exterior finish is "
  922.     "that of smooth sandstone blocks, common in important buildings of the "
  923.     "era. A wide staircase leads up to double wooden doors with diamond-paned "
  924.     "windows. The doors are bordered by a pair of sandstone pillars, which "
  925.     "support a similar cap, carved with a pair of lions rampant. At the very "
  926.     "top of the building, you can see granite gargoyles on each corner. A "
  927.     "plaque by the door reads \"Daily News\".").
  928. AddEastChecker(r_ne2, newsEnter, false).
  929. AddEnterChecker(r_ne2, newsEnter, false).
  930. AddUpChecker(r_ne2, newsEnter, false).
  931. AddWestChecker(r_newsRoom, newsExit, false).
  932. AddExitChecker(r_newsRoom, newsExit, false).
  933. Sign(r_ne2, "plaque.door;plaque,by,the", "", "\"Daily News\"").
  934. Scenery(r_ne2,
  935.     "gargoyle;granite."
  936.     "building;old."
  937.     "block;smooth,sandstone."
  938.     "staircase;wide."
  939.     "door;double,wooden."
  940.     "window;diamond-paned,diamond,paned."
  941.     "pillar;sandstone."
  942.     "cap;sandstone."
  943.     "lion;rampant."
  944.     "rampant;lions,lion").
  945.  
  946. define tp_news proc drawXXXXRoom(string which)void:
  947.     int i;
  948.  
  949.     GAMove(nil, 16, 24);
  950.     for i from 0 upto 3 do
  951.     GSetPen(nil, i + 1);
  952.     GRectangle(nil, 128 - 2 * i, 52 - 2 * i, false);
  953.     GRMove(nil, 1, 1);
  954.     od;
  955.     GSetPen(nil, C_GOLD);
  956.     GAMove(nil, 28, 42);
  957.     GText(nil, "Read and post");
  958.     GAMove(nil, 36, 52);
  959.     GText(nil, "usenet ");
  960.     GText(nil, which);
  961.     GAMove(nil, 64, 62);
  962.     GText(nil, "here");
  963. corp;
  964.  
  965. define tp_news NEWSROOM_ID NextEffectId().
  966. define tp_news proc drawNewsRoom()void:
  967.  
  968.     if not KnowsEffect(nil, NEWSROOM_ID) then
  969.     DefineEffect(nil, NEWSROOM_ID);
  970.     GSetImage(nil, "newsroom");
  971.     IfFound(nil);
  972.         GShowImage(nil, "", 0, 0, 160, 100, 0, 0);
  973.     Else(nil);
  974.         drawXXXXRoom("news");
  975.     Fi(nil);
  976.     EndEffect();
  977.     fi;
  978.     CallEffect(nil, NEWSROOM_ID);
  979. corp;
  980.  
  981. RoomGraphics(r_newsRoom, "NewsRoom", "", NextMapGroup(), 0, 0, drawNewsRoom).
  982.  
  983. unuse t_streets
  984.